package org.cloudplatform.common.feign;

import feign.Feign;
import feign.Logger;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.gson.GsonDecoder;
import feign.gson.GsonEncoder;
import feign.slf4j.Slf4jLogger;
import org.cloudplatform.common.feign.anno.FeignClient;
import org.cloudplatform.common.feign.register.InstanceRegister;
import org.cloudplatform.common.feign.ribbon.MyRibbonClient;
import org.cloudplatform.common.feign.zk.ServiceInstanceZookeeper;
import org.cloudplatform.common.feign.zk.SubscriptionClient;

import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 类描述：feign的构建器
 * 作者：徐卫超 (cc)
 * 时间 2022/5/13 15:02
 */
public class FeignCluster {
    private final Map<String, Object> feignCache = new ConcurrentHashMap<>();
    /**
     * 创建一个Zookeeper连接程序
     */
    private SubscriptionClient subscriptionClient;
    /**
     * 创建一个注册程勋
     */
    private InstanceRegister instanceRegister;
    /**
     * 定义序列化对象
     */
    private Decoder decoder;
    /**
     * 定义序列化对象
     */
    private Encoder encoder;

    /**
     * 是否开启debug模式 默认不开启
     */
    private boolean isDebug;

    private boolean isLoadBalance = false;


    /**
     * 创建一个HTTP请求
     *
     * @param clazz http接口对象
     * @param <T>   接口对象的泛型
     * @return 返回创建的代理对象
     */
    @SuppressWarnings("unchecked")
    public <T> T createHttpClient(Class<T> clazz) {
        if (clazz == null) {
            throw new RuntimeException("无效的class对象");
        }
        if (isLoadBalance) {
            return (T) feignCache.computeIfAbsent(clazz.getName(), val -> doServiceCreate(clazz));
        } else {
            return (T) feignCache.computeIfAbsent(clazz.getName(), val -> doCreate(clazz));
        }
    }


    /**
     * 创建一个不带负载的HTTP构建器
     *
     * @return 返回HTTP构建器
     */
    public static FeignCluster create() {
        FeignCluster cluster = new FeignCluster();
        cluster.decoder = new GsonDecoder();
        cluster.encoder = new GsonEncoder();
        cluster.isDebug = false;
        return cluster;
    }

    /**
     * 利用zookeeper注册中心的调用类
     *
     * @param zkAddress     zk的连接地址
     * @param subscribePath 微服务注册中心的地址
     * @return 返回带有注册中心的HTTP构建器
     */
    public static FeignCluster createZookeeperCluster(String zkAddress, String subscribePath) {
        FeignCluster cluster = new FeignCluster();
        // 连接zk
        ServiceInstanceZookeeper serviceInstanceZookeeper = new ServiceInstanceZookeeper();
        cluster.subscriptionClient = serviceInstanceZookeeper;
        cluster.instanceRegister = serviceInstanceZookeeper;
        Properties config = new Properties();
        config.put("address", zkAddress);
        cluster.subscriptionClient.connect(config);
        cluster.subscriptionClient.subscribe(subscribePath);
        cluster.subscriptionClient.completed();
        // feign相关组件
        cluster.decoder = new GsonDecoder();
        cluster.encoder = new GsonEncoder();
        cluster.isDebug = true;
        cluster.isLoadBalance = true;
        return cluster;
    }

    /**
     * 利用zookeeper注册中心构建的一个调用类
     *
     * @param zkAddress zk的连接地址
     * @return 返回带有注册中心的HTTP构建器
     */
    public static FeignCluster createZookeeperCluster(String zkAddress) {
        return createZookeeperCluster(zkAddress, "/services");
    }

    private <T> T doServiceCreate(Class<T> clazz) {
        FeignClient annotation = clazz.getAnnotation(FeignClient.class);
        if (annotation == null) {
            throw new RuntimeException("接口上被标注 FeignClient 注解");
        }

        if (hasText(annotation.id()) && isLoadBalance) {
            throw new RuntimeException("请输入有效的服务名或有效的根路径");
        }
        return Feign.builder()
                .client(new MyRibbonClient(null, null, instanceRegister))
                .decoder(this.decoder)
                .encoder(this.encoder)
                .logLevel(isDebug ? Logger.Level.FULL : Logger.Level.NONE)
                .logger(new Slf4jLogger())
                .target(clazz, "http://" + annotation.id());
    }

    private <T> T doCreate(Class<T> clazz) {
        FeignClient annotation = clazz.getAnnotation(FeignClient.class);
        if (annotation == null) {
            throw new RuntimeException("接口上被标注 FeignClient 注解");
        }

        if (hasText(annotation.url()) && !isLoadBalance) {
            throw new RuntimeException("请输入有效的根路径");
        }
        return Feign.builder()
                .client(new MyRibbonClient(null, null, instanceRegister))
                .decoder(this.decoder)
                .encoder(this.encoder)
                .logLevel(isDebug ? Logger.Level.FULL : Logger.Level.NONE)
                .logger(new Slf4jLogger())
                .target(clazz, annotation.url());
    }

    private boolean hasText(String text) {
        return text != null && !text.isEmpty();
    }

}
